﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NeuralNetworks.ActivationFunctions;
using System.IO;
using System.Text.RegularExpressions;
using NeuralNetworks.FileParsing;
using NeuralNetworks.Networks;
using NeuralNetworks.Layers;
using NeuralNetworks.Neurons;
using NeuralNetworks.Learning;

namespace NeuralNetworks
{
    public partial class Form1 : Form
    {
        private ActivationNetwork _activationNetwork;
        private SomNetwork _somNetwork;
        private BindingList<LearningProperty> _learningParams = new BindingList<LearningProperty>();
        private BindingList<LearningProperty> _outLearningParams = new BindingList<LearningProperty>();
        int nInput = 2;
        //int nOutput = 1;
        int actvFun = 0;
        private int epoch = 0;

        public Form1()
        {
            int[] fakeTable = { 1, 2, 3 };
            _activationNetwork = new ActivationNetwork(2, 0, 1, fakeTable, new LinearFunction());
            InitializeComponent();
            AddDefaultLayer(0, _activationNetwork.outputLayer);
            SetLearingDefaults();
        }

        private void SetLearingDefaults()
        {
            
            _learningParams.Add(new LearningProperty(500, 0.06, 1, 1.0));
            _learningParams.Add(new LearningProperty(1000, 0.03, 1, 0.5));
            _learningParams.Add(new LearningProperty(1500, 0.015, 1, 0.25));
            _learningParams.Add(new LearningProperty(2000, 0.0075, 1, 0.125));
            _outLearningParams.Add(new LearningProperty(2000, 0));
            _outLearningParams.Add(new LearningProperty(12000, 0.15));
            _outLearningParams.Add(new LearningProperty(32000, 0.075));
            _outLearningParams.Add(new LearningProperty(72000, 0.0185));
            _outLearningParams.Add(new LearningProperty(152000, 0.00117));
            SomLearningParams.DataSource = _learningParams;
            OutLearningParamsGridView.DataSource = _outLearningParams;
        }

        private void restartAndControls()
        {
            NHiddenNumericUpDown.Value = _activationNetwork.HiddenLayersCount;
            printNetwork();
        }

        private void CreateNetworkButton_Click(object sender, EventArgs e)
        {
            int[] table = new int[(int)NHiddenNumericUpDown.Value];
            int i = 0;
            for (i = 0; i < NHiddenNumericUpDown.Value; i++)
            {
                string a = NeuronNumbersGrid[1,i].Value.ToString();
                table[i] = int.Parse(a);
            }
            _activationNetwork = new ActivationNetwork(2, (int)NHiddenNumericUpDown.Value, 1, table, new LinearFunction());
            for (i = 0; i < NHiddenNumericUpDown.Value; i++)
            {
                _activationNetwork.hiddenLayers[i].MinRandom = double.Parse(NeuronNumbersGrid[3, i].Value.ToString());
                _activationNetwork.hiddenLayers[i].MaxRandom = double.Parse(NeuronNumbersGrid[4, i].Value.ToString());
                _activationNetwork.hiddenLayers[i].ActvFunction = ActivationFunctionFactory.getFunction(NeuronNumbersGrid[2, i].Value.ToString());   
            }

            _activationNetwork.outputLayer.MinRandom = double.Parse(NeuronNumbersGrid[3, i].Value.ToString());
            _activationNetwork.outputLayer.MaxRandom = double.Parse(NeuronNumbersGrid[4, i].Value.ToString());
            _activationNetwork.outputLayer.ActvFunction = ActivationFunctionFactory.getFunction(NeuronNumbersGrid[2, i].Value.ToString()); 
            
            restartAndControls();
        }


        private void printNetwork()
        {
            PrintPanel.Controls.Clear();

            Button newClearButton = new Button();
            newClearButton.Text = "Czyść";
            newClearButton.Size = new Size(50, 22);
            newClearButton.Click += ClearPrintPanelButton_Click;
            PrintPanel.Controls.Add(newClearButton);

            PrintPanel.BackColor = Color.White;
            PrintPanel.BorderStyle = BorderStyle.FixedSingle;
            int x0 = PrintPanel.Location.X;
            int y0 = PrintPanel.Location.Y;

            int nHidden = _activationNetwork.hiddenLayers.Length;
            //MessageBox.Show("Liczba warstw: " + nHidden);
            for (int i = 0; i < nHidden; i++)
            {
                // i = i-ta warstwa
                addConnectionToPrintPanel(_activationNetwork.hiddenLayers[i], i);
            }

            addConnectionToPrintPanel(_activationNetwork.outputLayer, _activationNetwork.hiddenLayers.Length);
        }

        private void addConnectionToPrintPanel(Layer layer, int layerNumber)
        {
            int nneurons = layer.Neurons.Count;
            int lastY = 0;
            for (int j = 0; j < nneurons; j++)
            {
                Neuron neuron = layer.Neurons[j] as Neuron;
                // j = j-ty neuron
                Label neuronLabel = new Label();
                neuronLabel.Size = new Size(35, 15);
                // pozycjonowanie kontrolki
                neuronLabel.Location = new Point(110 + layerNumber * 110, j * neuron.Inputs.Count * 35);
                neuronLabel.Text = "N" + (layerNumber + 1).ToString() + "." + (j + 1).ToString();
                neuronLabel.BackColor = Color.Black;
                neuronLabel.ForeColor = Color.White;
                PrintPanel.Controls.Add(neuronLabel);

                BiasPanel biasPan = new BiasPanel(neuron);
                biasPan.Location = new Point(110 + layerNumber * 110, j * neuron.Inputs.Count * 35 + 15);
                PrintPanel.Controls.Add(biasPan);

                for (int c = 0; c < neuron.Inputs.Count; c++)
                {
                    // c = c-te wejście neuronu
                    ConnectionPanel connPan = new ConnectionPanel(layerNumber, c, j, neuron);
                    // pozycjonowanie kontrolki
                    lastY = c * 35 + j * neuron.Inputs.Count * 35;
                    connPan.Location = new Point(60 + layerNumber * 110, lastY);
                    PrintPanel.Controls.Add(connPan);
                }
            }

            /*BiasPanel biasPan = new BiasPanel(layerNumber, layer);
            biasPan.Location = new Point(60 + layerNumber * 110, lastY + 35);
            PrintPanel.Controls.Add(biasPan);*/
        }

        private void NHiddenNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            if (NHiddenNumericUpDown.Value < 0)
            {
                NHiddenNumericUpDown.Value = 0;
            }
            NeuronNumbersGrid.Rows.Clear();
            int i = 0;
            for (i = 0; i < NHiddenNumericUpDown.Value; i++)
            {
                AddDefaultLayer(i, new ActivationLayer(1, new LinearFunction(), ""));
            }

            AddDefaultLayer(i, _activationNetwork.outputLayer);
        }

        private void AddDefaultLayer(int i, ActivationLayer layer)
        {
            NeuronNumbersGrid.Rows.Add();
            NeuronNumbersGrid[0, i].Value = (i+1).ToString();
            NeuronNumbersGrid[1, i].Value = layer.Neurons.Count.ToString();
            NeuronNumbersGrid[2, i].Value = layer.ActvFunction.ToString();
            NeuronNumbersGrid[3, i].Value = layer.MinRandom.ToString();
            NeuronNumbersGrid[4, i].Value = layer.MaxRandom.ToString();
        }

        private void testButton_Click(object sender, EventArgs e)
        {
            testResultLabel.Text = "";
            try
            {
                double in1 = Double.Parse(input1TextBox.Text);
                double in2 = Double.Parse(input2TextBox.Text);
                double[] input = new double[nInput];
                input[0] = in1;
                input[1] = in2;
                double[] output = _activationNetwork.Compute(input);
                output1TextBox.Text = output[0].ToString();
            }
            catch (Exception)
            {
                testResultLabel.Text = "Złe dane!";
            }

        }

        private void randomWeightsButton_Click(object sender, EventArgs e)
        {
            _activationNetwork.RandomWeights(-1,1);
            printNetwork();
        }

        private void SetWeightsButton_Click(object sender, EventArgs e)
        {
            foreach (Control control in PrintPanel.Controls)
            {
                if (control.GetType() == typeof(ConnectionPanel))
                {
                    ConnectionPanel connPan = control as ConnectionPanel;
                    if (connPan.Layer >= 0)
                    {
                        Neuron neuron;
                        if (connPan.Layer < _activationNetwork.hiddenLayers.Length)
                        {
                            neuron = (_activationNetwork.hiddenLayers[connPan.Layer].Neurons[connPan.NeuronNumber] as Neuron);
                        }
                        else
                        {
                            neuron = (_activationNetwork.outputLayer.Neurons[connPan.NeuronNumber] as Neuron);
                        }
                        (neuron.Inputs[connPan.Input] as Connection).Weight = connPan.Weight;
                    }
                }
                else if (control.GetType() == typeof(BiasPanel))
                {
                    BiasPanel bp = control as BiasPanel;
                    bp.Neuron.BiasWeight = bp.Weight;
                }
            }
            restartAndControls();
            printNetwork();
        }

        private void ReadWights_Click(object sender, EventArgs e)
        {
            Stream myStream = null;
            OpenFileDialog openFileDialog1 = new OpenFileDialog();

            openFileDialog1.InitialDirectory = "D:\\programowanie\\neurony\\NeuralNetworks";
            openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            openFileDialog1.FilterIndex = 1;
            openFileDialog1.RestoreDirectory = true;

            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if ((myStream = openFileDialog1.OpenFile()) != null)
                    {
                        StreamReader stremReader = new StreamReader(myStream);
                        
                        Regex spaces = new Regex(@"\s+");
                        string[] layersDesc = spaces.Split(stremReader.ReadLine());

                        int inputs = int.Parse(layersDesc[0]);
                        int hiddenLayers = int.Parse(layersDesc[1]);
                        int outputs = int.Parse(layersDesc[2]);

                        int[] table = new int[hiddenLayers];
                        
                        for (int i = 0; i < hiddenLayers; i++)
                        {
                            table[i] = int.Parse(stremReader.ReadLine());
                        }

                        _activationNetwork = new ActivationNetwork(inputs, hiddenLayers, outputs, table, new LinearFunction());
                        int layers = 2 + hiddenLayers;
                        for (int i = 0; i < layers; i++)
                        {
                            string line = stremReader.ReadLine();
                            string[] parts = spaces.Split(line);

                            Layer current = null;
                            Layer next = null;
                            if (i == 0)
                            {
                                current = _activationNetwork.inputLayer;
                                if (layers == 2 || i == layers - 2)
                                {
                                    next = _activationNetwork.outputLayer;
                                }
                                else if (i < _activationNetwork.HiddenLayersCount)
                                {
                                    next = _activationNetwork.hiddenLayers[i];
                                }
                            }
                            else if (i == layers - 1)
                            {
                                current = _activationNetwork.outputLayer;
                            }
                            else if (i - 1 < _activationNetwork.HiddenLayersCount)
                            {
                                current = _activationNetwork.hiddenLayers[i-1];
                                if (i < _activationNetwork.HiddenLayersCount)
                                {
                                    next = _activationNetwork.hiddenLayers[i];
                                }
                                else
                                {
                                    next = _activationNetwork.outputLayer;
                                }
                            }

                            foreach (string part in parts)
                            {
                                Console.WriteLine(part);

                                LayerPart p = LayerPartFactory.getPart(part);

                                p.apply(current, next);

                            }
                        }
                        restartAndControls();
                        myStream.Close();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        }

        private void activationFunctions_SelectedIndexChanged(object sender, EventArgs e)
        {
            ComboBox combo = (ComboBox)sender;
            this.actvFun = combo.SelectedIndex;
            Console.WriteLine(combo.SelectedIndex);
        }

        private void NPatternsNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            NPatternsNumericUpDown.Value = 4;
        }

        private void numericUpDown3_ValueChanged(object sender, EventArgs e)
        {
            //SomKohXNumericUpDown.Value = 2;
        }

        private void numericUpDown2_ValueChanged(object sender, EventArgs e)
        {
            //SomKohYNumericUpDown.Value = 2;
        }

        private void InputDimensionXNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            //InputDimensionXNumericUpDown.Value = 3;
        }

        private void InputDimensionYNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            //InputDimensionYNumericUpDown.Value = 3;
        }

        private void PerceptronRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            GroupBox.Visible = true;
            SomGroupBox.Visible = false;
        }

        private void SomRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            GroupBox.Visible = false;
            SomGroupBox.Visible = true;
        }

        private void SomNetworkCreateButton_Click(object sender, EventArgs e)
        {
            if (!CounterPoropagationCheckBox.Checked)
            {
                string[] patterns = { SomPatternTextBox1.Text, SomPatternTextBox2.Text, SomPatternTextBox3.Text, SomPatternTextBox4.Text };
                _somNetwork = new SomNetwork(patterns.Length, patterns, (int)SomKohXNumericUpDown.Value, (int)SomKohYNumericUpDown.Value);
            }
            else
            {
                string[] patterns = { SomPatternTextBox1.Text, SomPatternTextBox2.Text, SomPatternTextBox3.Text, 
                                        SomPatternTextBox4.Text, SomPatternTextBox5.Text, SomPatternTextBox6.Text,
                                        SomPatternTextBox7.Text, SomPatternTextBox8.Text, SomPatternTextBox9.Text
                                    };

                List<List<int>> pClasses = new List<List<int>>();
                List<int> firstClass = new List<int>();
                firstClass.Add(0);
                firstClass.Add(1);
                firstClass.Add(2);
                List<int> secondClass = new List<int>();
                secondClass.Add(3);
                secondClass.Add(4);
                secondClass.Add(5);
                List<int> thirdClass = new List<int>();
                thirdClass.Add(6);
                thirdClass.Add(7);
                thirdClass.Add(8);
                pClasses.Add(firstClass);
                pClasses.Add(secondClass);
                pClasses.Add(thirdClass);
                IActivationFunction actvf;
                if (OutLearningRuleComboBox.SelectedIndex == 0)
                {
                    actvf = new LogisticFunction();
                }
                else
                {
                    actvf = new LinearFunction();
                }
                CounterPropagationNetwork cpn = new CounterPropagationNetwork(patterns.Length, pClasses, patterns,
                                                            (int)SomKohXNumericUpDown.Value, (int)SomKohYNumericUpDown.Value,
                                                            actvf);
                _somNetwork = cpn;
            }
            epoch = 0;
            EpochsInfoLabel.Text = "Epok: " + epoch;
            Console.WriteLine("Network created");
        }

        private void ClearPrintPanelButton_Click(object sender, EventArgs e)
        {
            PrintPanel.Controls.Clear();

            Button newClearButton = new Button();
            newClearButton.Text = "Czyść";
            newClearButton.Size = new Size(50, 22);
            newClearButton.Click += ClearPrintPanelButton_Click;
            PrintPanel.Controls.Add(newClearButton);
        }

        private void SomRandomWeightsButton_Click(object sender, EventArgs e)
        {
            _somNetwork.RandomWeights(-1.0, 1.0);
            epoch = 0;
            EpochsInfoLabel.Text = "Epok: " + epoch;
        }

        private void LearningButton_Click(object sender, EventArgs e)
        {
            
            int epochs = (int) Double.Parse(EpochsToLearnTextBox.Text);

            epoch += epochs;
            EpochsInfoLabel.Text = "Epok: " + epoch;
            SortedList<int, LearningProperty> properties = new SortedList<int, LearningProperty>();
            foreach (LearningProperty lp in _learningParams)
            {
                properties.Add(lp.Epochs, lp);
            }
            _somNetwork.Learn(properties, epochs);

            SomWeightTextBox.Text = "";
            for (int i = 0; i < _somNetwork.KohonenLayer.Neurons.Count; i++) 
            {
                SomNeuron n = _somNetwork.KohonenLayer.Neurons[i] as SomNeuron;
                for (int j = 0; j < n.Inputs.Count; j++)
                {
                    Connection c = n.Inputs[j] as Connection;
                    SomWeightTextBox.Text += String.Format("{0:0.000}", c.Weight) + ";\t";
                    if((j+1)%3 == 0)
                        SomWeightTextBox.Text += Environment.NewLine;
                }
                SomWeightTextBox.Text += Environment.NewLine;
            }
        }

        private void SomInputButton_Click(object sender, EventArgs e)
        {
            // Format input
            string inputString = SomInputTextBox.Text.Replace(Environment.NewLine, "");
            double[] input = new double[inputString.Length];
            for (int i = 0; i < input.Length; i++)
            {
                input[i] = Double.Parse(inputString[i].ToString());
            }
            double [] output =_somNetwork.Compute(input);
            SomOutputTextBox.Text = "";
            for(int i = 0; i < output.Length; i++)
            {
                SomOutputTextBox.Text += Math.Round(output[i],3) + ";   ";
            }

        }

        private void SomZeroWeightsButton_Click(object sender, EventArgs e)
        {
            _somNetwork.ZeroWeights();
            epoch = 0;
            EpochsInfoLabel.Text = "Epok: " + epoch;
        }

        private void CounterPoropagationCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            CheckBox s = (CheckBox)sender;
            OutLearningGroupBox.Enabled = s.Checked;
            NClassesNumericUpDown.Enabled = s.Checked;

        }

        private void LearnOutButton_Click(object sender, EventArgs e)
        {
            CounterPropagationNetwork cpn = (CounterPropagationNetwork)_somNetwork;
            int ep = Int32.Parse(OutLearnEpochsText.Text.ToString());
            SortedList<int, LearningProperty> properties = new SortedList<int, LearningProperty>();
            foreach (LearningProperty lp in _outLearningParams)
            {
                properties.Add(lp.Epochs, lp);
            }
            double error = cpn.LearnOut(properties, ep);
            OutEpochsLearnedLabel.Text = "Epok: " + ep.ToString();
            OutErrorLable.Text = "Wynik uczenia: " + Math.Round(error, 4).ToString();
            Console.WriteLine(error);
        }

        private void simpleLearn_Click(object sender, EventArgs e)
        {
            BackPropagationLearning bpl = new BackPropagationLearning(_activationNetwork);
            bpl.LearningRate = 0.8;
            bpl.Momentum = 0.3;

            double[][] inputs = new double[4][] { new double[] {1,0},
                                                    new double[] {1,1},
                                                    new double[] {0,0},
                                                    new double[] {0,1}
            } ;
            double[][] outputs = new double[4][] {
                                                new double[]{1},
                                                new double[]{0},
                                                new double[]{0},
                                                new double[]{1}
            };

            for (int i = 0; i < 10000; i++)
            {
                Console.WriteLine(bpl.RunEpoch(inputs, outputs));
            }

            restartAndControls();
            printNetwork();
        }

        

    }
}
